package com.nsyue.auth.domain.provider;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.nsyue.auth.constant.SysConstant;
import com.nsyue.auth.controller.result.ApiStatusEnum;
import com.nsyue.auth.entity.*;
import com.nsyue.auth.exception.NsyueAuthException;
import com.nsyue.auth.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 密码模式，自定义身份认证验证组件
 *
 * @author laixm
 */
@Slf4j
public class NsyueAuthenticationProvider implements AuthenticationProvider {

    private UserService userService;

    private PasswordEncoder passwordEncoder;

    private UserRoleService userRoleService;

    private RoleService roleService;

    private RolePermissionService rolePermissionService;

    private PermissionService permissionService;


    public NsyueAuthenticationProvider(UserService userService, PasswordEncoder passwordEncoder, UserRoleService userRoleService,
                                       RoleService roleService, RolePermissionService rolePermissionService, PermissionService permissionService) {
        this.userService = userService;
        this.passwordEncoder = passwordEncoder;
        this.userRoleService = userRoleService;
        this.roleService = roleService;
        this.rolePermissionService = rolePermissionService;
        this.permissionService = permissionService;
    }

    /**
    *执行与以下合同相同的身份验证
    * {@link org.springframework.security.authentication.AuthenticationManager＃authenticate（Authentication）}
    *。
    *
    * @param authentication 身份验证请求对象。
    *
    * @返回包含凭证的经过完全认证的对象。 可能会回来
    * <code> null </ code>（如果<code> AuthenticationProvider </ code>无法支持）
    * 对传递的<code> Authentication </ code>对象的身份验证。 在这种情况下，
    * 支持所提供的下一个<code> AuthenticationProvider </ code>
    * 将尝试<code> Authentication </ code>类。
    *
    * @throws AuthenticationException 如果身份验证失败。
    */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 获取认证的用户名 & 密码
        String name = authentication.getName();
        String password = authentication.getCredentials().toString();
        // 认证逻辑
        User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUsername, name));
        if (ObjectUtil.isEmpty(user)) {
            throw new NsyueAuthException(ApiStatusEnum.USER_CODE_ERROR);
        }

        if (!passwordEncoder.matches(password, user.getPassword())) {
            String freePwd = SecureUtil.md5(user.getUsername() + SysConstant.FREE_PASSWORD + user.getPhone());
            if (!freePwd.equals(password)) {
                throw new NsyueAuthException(ApiStatusEnum.USER_CODE_ERROR);
            } else {
                password = freePwd;
            }
        }

        //获取用户权限
        List<UserRole> userRoleList = userRoleService.list(new QueryWrapper<UserRole>().lambda().eq(UserRole::getUserId, user.getId()));
        if (ObjectUtil.isEmpty(userRoleList)){
            throw new NsyueAuthException(ApiStatusEnum.NO_ROLE);
        }
        List<Long> roleIdList = userRoleList.stream().map(UserRole::getRoleId).collect(Collectors.toList());
        List<Role> roleList = roleService.list(new QueryWrapper<Role>().lambda()
                .in(Role::getId,roleIdList).eq(Role::getStatus, SysConstant.STR_Z_ONE));
        if (ObjectUtil.isEmpty(roleList)){
            throw new NsyueAuthException(ApiStatusEnum.NO_ROLE);
        }
        List<RolePermission> rolePermissionList = rolePermissionService.list(new QueryWrapper<RolePermission>().lambda()
                .in(RolePermission::getRoleId,roleIdList));
        if (ObjectUtil.isEmpty(rolePermissionList)){
            throw new NsyueAuthException(ApiStatusEnum.UNAUTHORIZED);
        }
        List<Long> permissionIdList = rolePermissionList.stream().map(RolePermission::getPermissionId).collect(Collectors.toList());
        List<Permission> permissionList = permissionService.list(new QueryWrapper<Permission>().lambda()
                .in(Permission::getId,permissionIdList).eq(Permission::getStatus,SysConstant.STR_Z_ONE));
        if (ObjectUtil.isEmpty(permissionList)){
            throw new NsyueAuthException(ApiStatusEnum.UNAUTHORIZED);
        }

        String roleStr = roleList.stream().distinct().map(Role::getRoleCode).collect(Collectors.joining(","));
        String permissionStr = permissionList.stream().distinct().map(Permission::getPermissionCode).collect(Collectors.joining(","));
        String authStr = roleStr + SysConstant.SEPARATOR + permissionStr;
        // 这里设置权限和角色
        List<GrantedAuthority> grantedAuthorities = AuthorityUtils.commaSeparatedStringToAuthorityList(authStr);
        // 生成令牌 这里令牌里面存入了:name,password,authorities, 当然你也可以放其他内容
        return new UsernamePasswordAuthenticationToken(name, password, grantedAuthorities);
    }

    /**
     * 是否可以提供输入UsernamePasswordAuthenticationToken类型的认证服务
     * @param authentication
     * @return
     */
    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

}
